package com.free4inno.knowledgems.service;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.UUID;

/**
 * Author: ZhaoHaotian.
 * Date: 2021/9/26.
 */
@Slf4j
@Service
public class FileService {
    // nfs地址是否被使用
    @Value("${file.nfs.used:false}")
    private Boolean nfsUsed;

    // 获取NFS的url
    @Value("${file.nfs.url:}")
    private String nfsUrl;

    // file save path
    @Value("${file.save.path}")
    private String fileSavePath;

    // file upload environment
    @Value("${file.environment}")
    private String fileEnvironment;

    public String uploadFile(MultipartFile file) {
        log.info(this.getClass().getName() + "----in----" + "上传文件（uploadFile）" + "----");

        String savePath = getSavePath();
        String oldFileName = file.getOriginalFilename();

        // hash文件夹（hash(filename)） + uuid文件名字，例如：6A/xxx.png
        String filePath = this.getUploadFileFolder(oldFileName); //由原文件名hash得到的文件夹
        String newFileName = this.getUploadFileName(oldFileName); //uuid得到的新文件名

        String resMsg = "";
        String resUrl = "";

        try {
            File tempFile = File.createTempFile(newFileName.substring(0, newFileName.lastIndexOf('.')),
                    newFileName.substring(newFileName.lastIndexOf('.')),
                    new File(savePath + filePath));
            file.transferTo(tempFile);
            resMsg = "success";
            resUrl = filePath + "/" + tempFile.getName();
            log.info(this.getClass().getName() + "----out----" + "上传文件成功" + "----");
        } catch (IOException e){
            resMsg = "sorry~, something wrong when save file~";
            log.info(this.getClass().getName() + "----out----" + "上传文件失败" + "----");
        } finally {
            if (resMsg.isEmpty()){
                resMsg = "sorry~, something wrong when save file~";
                log.info(this.getClass().getName() + "----out----" + "上传文件失败" + "----");
            }
        }

        // return saveUrl + resUrl;
        return resUrl;
    }

    public void downloadFile(String filename, String id, Boolean isImage, HttpServletResponse response) {
        log.info(this.getClass().getName() + "----in----" + "下载文件（downloadFile）" + "----");
        String savePath = getSavePath();
        String orginFilePath = savePath + "/" + id;
        File file = new File(orginFilePath);
        if (file.exists()) {
            InputStream inputStream = null;
            BufferedOutputStream outputStream = null;
            try {
                inputStream = new FileInputStream(file);
                byte[] buffer = new byte[1024];
                int byteread;
                try {
                    response.reset();
                    if (isImage) {
                        // 图片直接输出到浏览器
                        String formatType = id.substring(id.lastIndexOf(".") + 1, id.length());
                        response.setContentType("image/" + formatType);
                    } else {
                        // 附件执行下载任务
                        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
                        response.setContentType("application/octet-stream");
                    }
                    outputStream = new BufferedOutputStream(response.getOutputStream());
                    while ((byteread = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, byteread);
                    }
                    inputStream.close();
                    outputStream.flush();
                    outputStream.close();
                    log.info(this.getClass().getName() + "----out----" + "文件下载完毕" + "----");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                try {
                    if (outputStream != null) {
                        outputStream.close();
                    }
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        } else {
            response.reset();
            try {
                response.sendError(404,"文件不存在");
                log.info(this.getClass().getName() + "----out----" + "文件不存在" + "----");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public Boolean deleteFile(String saveUrl) {
        log.info(this.getClass().getName() + "----in----" + "删除文件（deleteFile）" + "----");

        // 由saveUrl取出fileId
        String fileId = "";
        if (saveUrl.startsWith("/uploadfiles/")) {
            // 旧本地项目目录文件 (/ )
            fileId = saveUrl.replace("/uploadfiles/", "");
        } else if (saveUrl.startsWith("../uploadfiles/")) {
            // 旧本地项目目录文件 (..)
            fileId = saveUrl.replace("../uploadfiles/", "");
        } else if (saveUrl.startsWith("/util/downloadImage")) {
            // 新图片文件 (/ )
            fileId = saveUrl.replace("/util/downloadImage?id=", "");
        } else if (saveUrl.startsWith("../util/downloadImage")) {
            // 新图片文件 (..)
            fileId = saveUrl.replace("../util/downloadImage?id=", "");
        } else if (saveUrl.startsWith("/util/downloadAttachment")) {
            // 新附件文件 (/ )
            fileId = saveUrl.replace("/util/downloadAttachment?id=", "");
        } else if (saveUrl.startsWith("../util/downloadAttachment")) {
            // 新附件文件 (..)
            fileId = saveUrl.replace("../util/downloadAttachment?id=", "");
        } else {
            if (saveUrl.startsWith(nfsUrl) && nfsUsed) {
                // 旧nfs文件
                fileId = saveUrl.replace(nfsUrl, "");
            } else {
                log.info(this.getClass().getName() + "----out----" + "删除文件saveUrl解析错误" + "----");
                return false;
            }
        }

        String savePath = getSavePath();
        File file = new File(savePath + fileId);
        if (file.delete()){
            log.info(this.getClass().getName() + "----out----" + "删除文件成功" + "----");
            return true;
        } else {
            log.info(this.getClass().getName() + "----out----" + "删除文件失败" + "----");
            return false;
        }
    }

    public String getSavePath() {
        // path & OS
        String savePath = "";
        if ( fileEnvironment.equals("dev") ) {
            // get system run path
            String runPath = System.getProperty("user.dir");
            savePath = runPath + fileSavePath;
        } else if (fileEnvironment.equals("prod")){
            savePath = fileSavePath;
        }
        return savePath;
    }

    // 获取需要上传文件的文件夹
    public String getUploadFileFolder(String fileName) {
        String folderIdx = "0";
        // hash文件夹（hash(filename)） + uuid文件名字，例如：6A/xxx.png
        int unixSep = fileName.lastIndexOf(47);
        int winSep = fileName.lastIndexOf(92);
        int pos = Math.max(winSep, unixSep);
        if (pos != -1) {
            fileName = fileName.substring(pos + 1);
        }
        String originalFolder = MD5(fileName).substring(0, 2);
        String primacy = originalFolder.substring(0,1); //获取首字母。
        if (primacy.compareTo(folderIdx) > 0) {  //如果当前的首字母大于指定的字母
            Integer len = CHAR_TO_INT_BIMAP.get(folderIdx);
            Integer thisLen = CHAR_TO_INT_BIMAP.get(primacy);
            Integer idx = thisLen % len;
            primacy = INT_TO_CHAR_BIMAP.get(idx+1);
            originalFolder = primacy + originalFolder.substring(1);
        }
        return originalFolder; //由原文件名hash得到的文件夹
    }

    // 获取需要上传文件的文件名
    public String getUploadFileName(String fileName) {
        // hash文件夹（hash(filename)） + uuid文件名字，例如：6A/xxx.png
        String newFileName = UUID.randomUUID().toString().replace("-", "") + fileName.substring(fileName.lastIndexOf(46)); //uuid得到的新文件名
        return newFileName;
    }

    // MD5
    private static String MD5(String key) {
        char[] hexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        try {
            byte[] btInput = key.getBytes();
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            mdInst.update(btInput);
            byte[] md = mdInst.digest();
            int j = md.length;
            char[] str = new char[j * 2];
            int k = 0;
            for(int i = 0; i < j; ++i) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 15];
                str[k++] = hexDigits[byte0 & 15];
            }
            return new String(str);
        } catch (Exception var10) {
            return null;
        }
    }

    // 首字母 : 首字母对应的 idx(len)
    private static final HashMap<String, Integer> CHAR_TO_INT_MAP = new HashMap<String, Integer>(){
        {
            // 0:1 , 1:2 , ... , F:16
            for (int i = 0; i <= 9; i++){
                put(String.valueOf(i), i + 1);
            }
            for (int i = 10; i < 16; i++){
                put(String.valueOf((char)(i + 55)), i + 1);
            }
        }
    };

    public static final BiMap<String, Integer> CHAR_TO_INT_BIMAP = HashBiMap.create(CHAR_TO_INT_MAP);

    public static final BiMap<Integer, String> INT_TO_CHAR_BIMAP = CHAR_TO_INT_BIMAP.inverse();


}
